package edu.mapi.aamas.behaviors;

import jade.core.AID;
import jade.core.Agent;
import jade.domain.FIPANames;
import jade.lang.acl.ACLMessage;
import jade.proto.ContractNetInitiator;

import java.awt.Color;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

import edu.mapi.aamas.common.Proposal;
import edu.mapi.aamas.common.Task;
import edu.mapi.aamas.operator.OperatorAgent;
/**
 * This behavior implements FIPA contract net protocol to send a task request to all agents
 * subscribing to the "tasks" topic, handling all proposals and choosing one thereafter.
 * @author ZP
 */
public class TaskInitiator extends ContractNetInitiator {
	
	
	
	public TaskInitiator(Agent a, Task task, String[] vehicleNames) {
		super(a, createMessage(a, task, vehicleNames));
		if (myAgent instanceof OperatorAgent)
			((OperatorAgent)myAgent).appendMessage("Requesting task "+task+" to "+vehicleNames.length+" vehicles. Waiting for proposals...", Color.blue.darker());
		
	}

	private static ACLMessage createMessage(Agent a, Task task, String[] vehicles) {
		ACLMessage msg = new ACLMessage(ACLMessage.CFP);
		msg.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
		msg.setReplyByDate(new Date(System.currentTimeMillis() + 3000));

		for (int i = 0; i < vehicles.length; i++)
			msg.addReceiver(new AID(vehicles[i], AID.ISLOCALNAME));
		
		try {
			msg.setContentObject(task);
			return msg;
		}
		catch (Exception e) {
			return null;
		}
	}
	
	protected void handlePropose(ACLMessage propose, Vector v) {
		System.err.println("!!!! Agent "+propose.getSender().getName()+" proposed "+propose.getContent());
		
	}
	
	protected void handleRefuse(ACLMessage refuse) {
		System.out.println("Agent "+refuse.getSender().getName()+" refused");
	}
	
	protected void handleFailure(ACLMessage failure) {
		if (failure.getSender().equals(myAgent.getAMS())) {
			System.out.println("Responder does not exist");
		}
		else {
			System.out.println("Agent "+failure.getSender().getName()+" failed");
		}
		// Immediate failure --> we will not receive a response from this agent
		//nResponders--;
	}
	
	
	
	protected void handleAllResponses(Vector responses, Vector acceptances) {

		if (myAgent instanceof OperatorAgent)
			((OperatorAgent)myAgent).appendMessage("Received "+responses.size()+" proposals...", Color.blue.darker());
	
		// Evaluate proposals.
		Proposal bestProposal = new Proposal("?", "?", Double.MAX_VALUE, Double.MIN_VALUE);
		AID bestProposer = null;
		ACLMessage accept = null;
		Enumeration e = responses.elements();
		while (e.hasMoreElements()) {
			
			ACLMessage msg = (ACLMessage) e.nextElement();
			
			if (msg.getPerformative() == ACLMessage.PROPOSE) {
				ACLMessage reply = msg.createReply();				
				reply.setPerformative(ACLMessage.REJECT_PROPOSAL);
				acceptances.addElement(reply);
				try {
					reply.setContentObject(msg.getContentObject());
					Proposal proposal = (Proposal) msg.getContentObject();
					//System.err.println("Inspecting proposal "+proposal);
					if (bestProposal == null || proposal.compareTo(bestProposal) > 0) {
						bestProposal = proposal;
						bestProposer = msg.getSender();
						accept = reply;
					}
					else {
						//System.out.println(">>>>"+proposal+" from "+msg.getSender()+" is worse than "+bestProposal+" from "+bestProposer);
					}
				}
				catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
		// Accept the proposal of the best proposer
		if (accept != null) {
			
			if (myAgent instanceof OperatorAgent)
				((OperatorAgent)myAgent).appendMessage("Best proposal is from "+bestProposer.getLocalName()+" "+bestProposal, Color.green.darker());
			
			accept.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
		}	
		else {
			if (myAgent instanceof OperatorAgent) {
				((OperatorAgent)myAgent).appendMessage("No proposal could be accepted ", Color.red.darker());
			}			
		}
	}
	
	protected void handleInform(ACLMessage inform) {
		System.out.println("Agent "+inform.getSender().getName()+" successfully performed the requested action");
	}
}
